`timescale 1ns / 1ps

// FIFO
module zq_fifo_ctrl
#(
    parameter DEPTH = 192
)
(
    input   clk,
    input   rst,

    input   in_vld,
    output  in_rdy,

    output  write_vld,
    output  [$clog2(DEPTH)-1 : 0]    write_addr,

    input   read_rdy,
    output  read_vld,
    output  [$clog2(DEPTH)-1 : 0]    read_addr
);

localparam ADDR_WIDTH = $clog2(DEPTH);

wire [ADDR_WIDTH-1: 0] addr_w;
wire last_w;
wire [ADDR_WIDTH-1: 0] addr_r;
wire last_r;

wire addr_neq;
wire flag_neq;
wire empty_n;
wire  full_n;
wire ena_w;
wire ena_r;

reg  flag_w;
reg  flag_r;

assign in_rdy = full_n;
assign write_vld  =  ena_w;
assign write_addr = addr_w;
assign read_vld  =  ena_r;
assign read_addr = addr_r;

assign addr_neq = (addr_w != addr_r);
assign flag_neq = (flag_w != flag_r);
assign empty_n = flag_neq | addr_neq;
assign full_n = ~flag_neq | addr_neq;

assign ena_w =  full_n & in_vld;
assign ena_r = empty_n & read_rdy;

always @(posedge clk)
begin
    if (rst)
    begin
        flag_w <= 1'b0;
        flag_r <= 1'b0; 
    end
    else
    begin
        if (ena_w)
            flag_w <= flag_w ^ last_w;
        if (ena_r)
            flag_r <= flag_r ^ last_r;
    end
end

zq_counter #(
    .N ( DEPTH )
)
inst_wtptr (
    .clk                     ( clk     ),
    .rst                     ( rst     ),
    .clken                   ( ena_w   ),
    .last                    ( last_w  ),
    .out                     ( addr_w  )
);

zq_counter #(
    .N ( DEPTH )
)
inst_rdptr (
    .clk                     ( clk     ),
    .rst                     ( rst     ),
    .clken                   ( ena_r   ),
    .last                    ( last_r  ),
    .out                     ( addr_r  )
);

endmodule
